package com.maikang.exammall.service;

import com.maikang.exammall.converter.JpaConverterIntegerSetBinaryInteger;
import com.maikang.exammall.entity.Hospital;
import com.maikang.exammall.entity.dto.HospitalIdLowestPriceOnly;
import com.maikang.exammall.repository.HospitalRepository;
import com.maikang.exammall.repository.PackageRepository;
import com.maikang.exammall.util.BitUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;


@Log4j2
@Service
@RequiredArgsConstructor
public class HospitalService extends BaseService<Hospital, Long, HospitalRepository> {
    private final PackageRepository packageRepository;

    public Slice<Hospital> findAll(Hospital hospital, Pageable pageable) {
        assert pageable != null;

        Specification<Hospital> specification = (Specification<Hospital>) (root, query, cb) -> {
            //用于暂时存放查询条件的集合
            List<Predicate> predicatesList = new ArrayList<>();
            if (hospital == null) return cb.and();

            if (StringUtils.hasLength(hospital.getName())) {
                Predicate name = cb.like(root.get("name"), '%' + hospital.getName() + '%');
                predicatesList.add(name);
            }


            if (hospital.getLevel() != null) {
                Predicate level = cb.equal(root.get("level"), hospital.getLevel());
                predicatesList.add(level);
            }

            if (hospital.getCityId() != null) {
                predicatesList.add(cb.equal(root.get("cityId"), hospital.getCityId()));
            }

            if (!ObjectUtils.isEmpty(hospital.getOpenDates())) {
                JpaConverterIntegerSetBinaryInteger convert = new JpaConverterIntegerSetBinaryInteger();
                Integer integer = convert.convertToDatabaseColumn(hospital.getOpenDates());
                Set<Integer> bitUtil = new BitUtil(integer).getCombinations();
                CriteriaBuilder.In<Integer> openDates = cb.in(root.get("openDates"));
                for (Integer integer1 : bitUtil) {
                    openDates.value(integer1);
                }
                predicatesList.add(openDates);
            }

            if (hospital.getPriceIntervalDTO() != null) {
                Join<Hospital, Package> joinPackage = root.join("packages", JoinType.INNER);
                if (hospital.getPriceIntervalDTO().getLeftPrice() != null) {
                    Predicate leftPrice = cb.ge(joinPackage.get("priceReal"), hospital.getPriceIntervalDTO().getLeftPrice());
                    predicatesList.add(leftPrice);
                }
                if (hospital.getPriceIntervalDTO().getRightPrice() != null) {
                    Predicate rightPrice = cb.le(joinPackage.get("priceReal"), hospital.getPriceIntervalDTO().getRightPrice());
                    predicatesList.add(rightPrice);
                }
                query.groupBy(root.get("id")); // deduplicate.
            }
            Predicate[] predicates = new Predicate[predicatesList.size()];
            return cb.and(predicatesList.toArray(predicates));
        };

        Slice<Hospital> allSliced = repository.findAllSliced(specification, pageable);
        addLowestRealPrice(allSliced.getContent());
        return allSliced;
    }

    public void addLowestRealPrice(List<Hospital> hospitals) {
        List<Long> hospitalIds = hospitals.stream().map(Hospital::getId).collect(Collectors.toList());
        Collection<HospitalIdLowestPriceOnly> idLowestPrice = packageRepository.findLowestRealPriceByHospitalId(hospitalIds);
        HashMap<Long, HospitalIdLowestPriceOnly> priceMap = new HashMap<>();
        for (HospitalIdLowestPriceOnly only : idLowestPrice) {
            priceMap.put(only.getHospitalId(), only);
        }

        for (Hospital h : hospitals) {
            h.setLowestRealPrice(priceMap.getOrDefault(h.getId(), new HospitalIdLowestPriceOnly() {
                @Override
                public Long getHospitalId() {
                    return null;
                }

                @Override
                public Long getLowestRealPrice() {
                    return 0L;
                }
            }).getLowestRealPrice());
        }
    }
}
